#include "widget.h"
#include <QDebug>
#include <QPainter>
#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    initialize_ffmpeg();

    desktopFmtCtx = avformat_alloc_context(); //分配format上下文, 用于保存输入格式的信息和状态, AVFormatContext 是 FFmpeg 用于管理多媒体文件或流的上下文结构
    const AVInputFormat *inputFormat = av_find_input_format("gdigrab"); //寻找输入设备gdigrab作为输入格式
    AVDictionary* options = NULL;
    av_dict_set(&options,"framerate","30", 0); //设置帧率为30
    av_dict_set(&options, "video_size", "1920x1080", 0); // 设置捕获分辨率
    if(avformat_open_input(&desktopFmtCtx, "desktop", inputFormat, &options) < 0) //开启输入设备
    {
        //关于avformat_open_input，它的功能是打开一个输入流(本地媒体文件路径、网络流地址等)并读取其头部信息，这里是打开本地的"desktop"
        qDebug()<<"avformat_open_input::can not open desktop.";
        return ;
    }

    //加载desktop的流信息
    if(avformat_find_stream_info(desktopFmtCtx, nullptr) < 0)
    {
        /*
         * avformat_find_stream_info 用于读取媒体文件的包（packet）以获取流信息
         * 填充流信息：avformat_find_stream_info 会读取文件的部分数据包以提取每个流（如视频流、音频流、字幕流等）的详细信息。这些信息包括编解码器类型、比特率、帧率、分辨率等。
         *              AVFormatContext 的 streams 数组会被填充，每个 AVStream 结构体将包含该流的详细信息。
         * 计算实际帧率：如果流中包含视频数据，函数会尝试计算实际的帧率，尤其是对于 MPEG-2 的重复帧模式，这对于正确解码和播放视频非常重要。
         * 填充编解码器参数：每个流的编解码器参数（如 codecpar 字段）会被填充，包括编码格式、采样率、声道数、宽度、高度等信息。
         * 设置时基（time base）：每个流的时基会被设置，用于时间戳的计算和转换。时基信息对于正确解码和同步音视频流至关重要。
         * 估算比特率：bit_rate 字段可能会被估算和设置，表示整个媒体文件的比特率。
        */
        qDebug()<<"avformat_find_stream_info::can not find stream information.";
        return ;
    }

    //找到format上下文（desktop）中的视频流索引
    for(uint i = 0; i < desktopFmtCtx->nb_streams; i++)
        if(desktopFmtCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            videoIndex=i; break;
        }
    if(videoIndex == -1){
        qDebug()<<"can not find video stream.";
        return ;
    }
    desktopCodecPara = desktopFmtCtx->streams[videoIndex]->codecpar; //找到format上下文中的视频流的编解码器参数
    desktopCodecCtx = avcodec_alloc_context3(nullptr);
    avcodec_parameters_to_context(desktopCodecCtx, desktopCodecPara); //根据编解码器参数，填充编解码器的上下文
    const AVCodec* codec = avcodec_find_decoder(desktopCodecPara->codec_id); //根据编解码器参数，找到对应的解码器

    qDebug() << "当前使用的解码器: " << codec->name; //输出是:bmp

    if(codec == nullptr){
        qDebug()<<"can not find codec";
        return ;
    }
    if(avcodec_open2(desktopCodecCtx, codec, nullptr) < 0){ //开启解码器
        qDebug()<<"can not open codec";
        return ;
    }

    packet=av_packet_alloc();
    frame=av_frame_alloc();

    //创建并返回一个 SwsContext 上下文，这是一个包含图像缩放/像素格式转换所需信息的结构体
    imgConvertContext = sws_getContext(desktopCodecPara->width, desktopCodecPara->height, desktopCodecCtx->pix_fmt, //源图像格式
                                       desktopCodecPara->width, desktopCodecPara->height, AV_PIX_FMT_RGB24, //目标图像格式：RGB
                                       SWS_BICUBIC, //指定用于重采样的算法，这里是双三次插值缩放
                                       nullptr, nullptr, nullptr);
    image = QImage(desktopCodecPara->width, desktopCodecPara->height, QImage::Format_RGB888);
    //av_image_fill_linesizes用于计算图像每个平面的行大小linesize，即每行像素所占的字节数。不同平面可能有不同的行大小。
    av_image_fill_linesizes(lineSize, AV_PIX_FMT_RGB24, desktopCodecPara->width);

    QTimer *timer=new QTimer; //定时刷新
    connect(timer, &QTimer::timeout, this, static_cast<void (QWidget::*)()>(&QWidget::repaint));
    timer->setInterval(30);
    timer->start();

    resize(desktopCodecPara->width*0.6, desktopCodecPara->height*0.6);
}

Widget::~Widget()
{
    avformat_network_deinit();
}

void Widget::paintEvent(QPaintEvent *event)
{
    // 读取一个视频帧（从desktop），写到packet中
    if(av_read_frame(desktopFmtCtx, packet) < 0)
        return ;

    if(packet->stream_index == videoIndex) //过滤掉非视频流
    {
        // 发送数据包给解码器
        if(avcodec_send_packet(desktopCodecCtx, packet) != 0)
            return;
        // 接收解码后的帧，写到frame中
        if(avcodec_receive_frame(desktopCodecCtx, frame) != 0)
            return;

        // 转换图像格式
        uint8_t* dst[]={image.bits()};
        sws_scale(imgConvertContext, frame->data, frame->linesize, 0, desktopCodecPara->height, dst, lineSize);
        av_packet_unref(packet); //清空数据包

        // 用QPainter渲染图像
        QPainter painter(this);
        painter.fillRect(rect(), image.scaled(width(), height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
    }
}
